package mental_connect.backend.backend_ym.controller;

import lombok.extern.slf4j.Slf4j;
import mental_connect.backend.backend_ym.entity.AccountInfo;
import mental_connect.backend.backend_ym.repository.AccountInfoRepository;
import mental_connect.backend.backend_ym.service.JwtTokenService;
import mental_connect.backend.backend_ym.utils.Result;
import mental_connect.backend.backend_ym.utils.ResultGenerator;
import mental_connect.backend.backend_ym.utils.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
@RestController
public class TestController {
    @Autowired
    private AccountInfoRepository accountInfoRepository;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    @Qualifier("accountInfo")
    private RedisTemplate<String, AccountInfo> redisTemplate;

    @PreAuthorize("hasAuthority('ACCOUNT')")
    @GetMapping("/test")
    public String test(){
        return "hello";
    }

    @PreAuthorize("hasAuthority('ADMIN')")
    @GetMapping("/findAll")
    public Result getAll(){
        List<AccountInfo> list = accountInfoRepository.findAll();
        //避免密码明文显示
        Map<String, Object> collect = list.stream().collect(Collectors.toMap(AccountInfo::getId, accountInfo -> {
            Map<String, Object> map = new HashMap<>();
            map.put("account",accountInfo.getAccount());
            map.put("authority",accountInfo.getAuthority());
            return map;
        }));

        return ResultGenerator.genOkResult(collect);
    }

    @PostMapping("/register")
    public Result register(@RequestBody AccountInfo accountInfo){
        //加密处理
        List<String> authority = Optional.ofNullable(accountInfo.getAuthority())
                .filter(i -> !i.isEmpty())
                .orElseGet(() -> {
                    List<String> list = new ArrayList<>();
                    list.add("ACCOUNT");
                    return list;
                });
        accountInfo.setAuthority(authority);
        accountInfo.setPassword(passwordEncoder.encode(accountInfo.getPassword()));
        try {
            accountInfoRepository.save(accountInfo);
        } catch (Exception e) {
            log.error(e.getMessage());
            return ResultGenerator.genFailedResult("注册失败");
        }
        return ResultGenerator.genOkResult("注册成功");
    }

    @PostMapping("/login")
    public Result login(@RequestBody AccountInfo accountInfo){
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(accountInfo.getAccount(),accountInfo.getPassword());
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        Optional.ofNullable(authenticate).orElseThrow(() -> new RuntimeException("authenticate can not be null"));
        AccountInfo account = (AccountInfo)authenticate.getPrincipal();

        Set<String> keys = redisTemplate.keys("*" + account.getId());
        Optional.ofNullable(keys)
                .ifPresent(i -> i.forEach(k -> redisTemplate.delete(k)));

        //这里用时间的最主要的目的就是为了方便多人登录的时候可以不用全部删除redis数据
        //我做了唯一登录所以全部删除，可以参考JwtTokenFilter里面注释部分
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
        String formattedDateTime = now.format(formatter);
        //唯一的key，由当前时间跟accountId组合
        String key = formattedDateTime+"_"+account.getId();
        //生成token
        String token = JwtTokenService.generateToken(key);
        Map<String, String> map = new HashMap<>();
        map.put("token",token);
        //存入到redis，也可以存到数据库里，二选一，为了避免反复访问数据库直接存到redis
        redisTemplate.opsForValue().set(key,account, 1,TimeUnit.HOURS);
        return ResultGenerator.genOkResult(map);
    }

    @GetMapping("/refreshToken")
    public Result refreshToken(){
        deleteRedisKey();
        boolean checkRedisKey = checkRedisKey();
        if(checkRedisKey){
            return ResultGenerator.genFailedResult("刷新token失败");
        }
        return ResultGenerator.genOkResult("刷新token成功");
    }

    @PostMapping("/logout")
    public void logout(){
        //不用做任何处理，JwtLogoutSuccessHandler已经做完处理
    }

    //只能修改用户名密码，管理员才能修改权限
    @GetMapping("/update")
    public Result update(@RequestBody AccountInfo accountInfo){
        return Optional.ofNullable(accountInfo.getId())
                .map(i -> {
                    Optional<AccountInfo> one = accountInfoRepository.findById(i);
                    one
                            .map(o -> {
                                if(!StringUtils.isEmpty(accountInfo.getAccount())){
                                    o.setAccount(accountInfo.getAccount());
                                }
                                //密码加密
                                if (!StringUtils.isEmpty(accountInfo.getPassword())) {
                                    o.setPassword(passwordEncoder.encode(accountInfo.getPassword()));
                                    deleteRedisKey();
                                    boolean checkRedisKey = checkRedisKey();
                                    if (checkRedisKey) {
                                        return ResultGenerator.genFailedResult("更新失败");
                                    }
                                }
                                try {
                                    accountInfoRepository.save(o);
                                } catch (Exception e) {
                                    return ResultGenerator.genFailedResult("更新失败");
                                }
                                return ResultGenerator.genOkResult("更新成功");
                            })
                            .orElseGet(() -> ResultGenerator.genFailedResult("未找到用户"));
                    return ResultGenerator.genOkResult("更新成功");
                })
                .orElseGet(() -> ResultGenerator.genFailedResult("用户id不能为空"));
    }

    @PreAuthorize("hasAuthority('ADMIN')")
    @GetMapping("/updateByAdmin")
    public Result updateByAdmin(@RequestBody AccountInfo accountInfo){
        return Optional.ofNullable(accountInfo.getId())
                .map(i -> {
                    Optional<AccountInfo> one = accountInfoRepository.findById(i);
                    one
                            .map(o -> {
                                //不能使用BeanUtils，防止未传值将之前的属性覆盖
                                //BeanUtils.copyProperties(accountInfo,o);
                                //密码加密
                                if (!StringUtils.isEmpty(accountInfo.getPassword())) {
                                    o.setPassword(passwordEncoder.encode(accountInfo.getPassword()));
                                    deleteRedisKey();
                                    boolean checkRedisKey = checkRedisKey();
                                    if (checkRedisKey) {
                                        return ResultGenerator.genFailedResult("更新失败");
                                    }
                                }
                                //权限去重
                                if(!CollectionUtils.isEmpty(accountInfo.getAuthority())){
                                    List<String> collect = Stream.concat(o.getAuthority().stream(), accountInfo.getAuthority().stream())
                                            .distinct().collect(Collectors.toList());
                                    o.setAuthority(collect);
                                }
                                if(!StringUtils.isEmpty(accountInfo.getAccount())){
                                    o.setAccount(accountInfo.getAccount());
                                }
                                try {
                                    accountInfoRepository.save(o);
                                } catch (Exception e) {
                                    return ResultGenerator.genFailedResult("更新失败");
                                }
                                return ResultGenerator.genOkResult("更新成功");
                            })
                            .orElseGet(() -> ResultGenerator.genFailedResult("未找到用户"));
                    return ResultGenerator.genOkResult("更新成功");
                })
                .orElseGet(() -> ResultGenerator.genFailedResult("用户id不能为空"));
    }

    private void deleteRedisKey(){
        String key = (String) SecurityUtils.getAuthentication().getDetails();
        Optional.ofNullable(key)
                .ifPresent(id -> redisTemplate.delete(key));
    }

    private boolean checkRedisKey(){
        String key = (String) SecurityUtils.getAuthentication().getDetails();
        String[] split = key.split("_");
        Set<String> keys = redisTemplate.keys("*" + split[1]);
        return !Optional.ofNullable(keys).isPresent();
    }
}
